﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Linq;
using PostSharp.CodeModel;
using PostSharp.CodeWeaver;
using PostSharp.Collections;
using MicroContainer;

namespace MicroContainer.CompileTime.Generators
{
   internal class DelegatesCacheInitializerCodeGenerator : ICodeGenerator
   {
      private readonly TypeMappingDef _mappingDef;

      public DelegatesCacheInitializerCodeGenerator(TypeMappingDef mappingDef)
      {
         _mappingDef = mappingDef;
      }

      public void Generate(CodeGenerationContext ctx)
      {
         FieldDefDeclaration delegatesCache = ctx.ContainerType.Fields.GetByName("_delegatesCache");
         MethodDefDeclaration staticCnstructor = ctx.ContainerType.Methods.GetOneByName(".cctor");

         InstructionBlock block = staticCnstructor.MethodBody.RootInstructionBlock;
         InstructionWriter writer = new InstructionWriter();
         InstructionSequence seq = staticCnstructor.MethodBody.CreateInstructionSequence();
         block.AddInstructionSequence(seq, NodePosition.After, null);
         writer.AttachInstructionSequence(seq);

         WeavingHelper helper = new WeavingHelper(ctx.ContainerType.Module);

         writer.EmitInstructionField(OpCodeNumber.Ldsfld, delegatesCache);
         helper.GetRuntimeType(ctx.ContainerType.Module.FindType(_mappingDef.From, BindingOptions.Default), writer);
         if (_mappingDef.Name != null)
         {
            writer.EmitInstructionString(OpCodeNumber.Ldstr, _mappingDef.Name);
         }
         else
         {
            writer.EmitInstruction(OpCodeNumber.Ldnull);
         }
         writer.EmitInstruction(OpCodeNumber.Ldnull);
         writer.EmitInstructionMethod(OpCodeNumber.Ldftn,
                                      ctx.ContainerType.Methods.GetOneByName("Build_" +
                                                                             _mappingDef.UniqieId.ToString().Replace(
                                                                                "-", "_")));
         writer.EmitInstructionMethod(OpCodeNumber.Newobj,
                                      ctx.ContainerType.Module.FindType(typeof (BuildMethod), BindingOptions.Default).
                                         GetTypeDefinition().Methods.GetOneByName(".ctor").Translate(writer.MethodBody.Module));

         writer.EmitInstructionMethod(OpCodeNumber.Newobj,
                                      ctx.ContainerType.Module.FindType(typeof (Mapping), BindingOptions.Default).
                                         GetTypeDefinition().Methods.GetOneByName(".ctor").Translate(writer.MethodBody.Module));

         writer.EmitInstructionMethod(OpCodeNumber.Callvirt,
                                      ctx.ContainerType.Module.FindType(typeof (ArrayList), BindingOptions.Default).
                                         GetTypeDefinition().Methods.GetOneByName("Add").Translate(writer.MethodBody.Module));
         writer.EmitInstruction(OpCodeNumber.Pop);         
         writer.DetachInstructionSequence();
      }
   }
}
